/*
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 Created by Youssef Francis and Pepijn Oomen.
 
 */

#import "App.h"
#import "Security/Authorization.h"
#include <unistd.h>

@implementation App
NSTimer *fadeOutTimer;
NSTimer *fadeInTimer;

- (BOOL) isVulnerable {
	NSTask *osascript = [[NSTask alloc] init];
	[osascript setLaunchPath:@"/usr/bin/osascript"];
	[osascript setArguments:[NSArray arrayWithObject:@"-e tell app \"ARDAgent\" to do shell script \"whoami\""]];
	
	NSPipe *pipe = [NSPipe pipe];
	[osascript setStandardOutput: pipe];
	[osascript setStandardError: [NSPipe pipe]];
	
	[osascript launch];
	[osascript waitUntilExit];
	
	NSFileHandle *handle = [pipe fileHandleForReading];
	NSString *output = [[NSString alloc] initWithData:[handle availableData] encoding:NSASCIIStringEncoding];
	BOOL isVulnerable = [output isEqualToString:@"root\n"];
	[output release];
	[osascript release];
	
	return isVulnerable;
}

- (BOOL) isPartlyVulnerable {
	NSTask *defaults = [[NSTask alloc] init];
	[defaults setLaunchPath:@"/usr/bin/defaults"];
	[defaults setArguments:[NSArray arrayWithObjects:@"read", @"/System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Info", @"NSAppleScriptEnabled", NULL]];
	
	NSPipe *pipe = [NSPipe pipe];
	[defaults setStandardOutput: pipe];
	
	[defaults launch];
	[defaults waitUntilExit];
	
	NSFileHandle *handle = [pipe fileHandleForReading];
	NSString *output = [[NSString alloc] initWithData:[handle availableData] encoding:NSASCIIStringEncoding];
	BOOL isPartlyVulnerable = !([output isEqualToString:@"1\n"] || [output isEqualToString:@"YES\n"]);
	[output release];
	[defaults release];
	
	return isPartlyVulnerable;
}

- (void) checkExploit {
	if ([self isVulnerable]) {
		[level setDoubleValue:1];
		[pic setImage:[[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForImageResource:@"fail.png"]]];
		[fixButton setTitle:@"Click to Patch Exploit!"];
		[vuln setStringValue:@"Vulnerable"];
		[fixButton setEnabled:YES];
		[rmSuid setEnabled:YES];
	} else if ([self isPartlyVulnerable]) {
		[level setDoubleValue:2];
		[pic setImage:[[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForImageResource:@"fail.png"]]];
		[fixButton setTitle:@"Click to Patch Exploit!"];
		[vuln setStringValue:@"Partially Vulnerable"];
		[fixButton setEnabled:YES];
		[rmSuid setEnabled:YES];
	} else {
		[level setDoubleValue:3];
		[pic setImage:[[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForImageResource:@"joe.png"]]];
		[fixButton setTitle:@"Exploit has been Patched!"];
		[vuln setStringValue:@"Secure"];
		[fixButton setEnabled:NO];
		[rmSuid setEnabled:NO];
	}
}

- (void) checkLoop {
	int counter = 15;
	while (counter > 0 && [level doubleValue] < 3) {
		counter--;
		sleep(1);
		[self checkExploit];
	}
	[progress stopAnimation:nil];
	
	if (counter == 0) {
		NSBeginAlertSheet(@"Error", @"OK", nil, nil, window, nil, nil, nil, nil, @"ARD Patcher cannot verify that the exploit has been patched, please quit and re-run ARD Patcher.");
	}
}

- (void)awakeFromNib {
	[NSApp setDelegate:self];
	[window setDelegate:self];
	[window center];
	[window setAlphaValue:0];
	fadeInTimer = [[NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(fadeWindow:) userInfo:@"fade_in" repeats:YES] retain];
	[self checkExploit];
}

- (void) fadeWindow:(NSTimer*)timer {
	if ([[timer userInfo] isEqualToString:@"fade_in"]) {
		if ([window alphaValue] == 0) {
			[window makeKeyAndOrderFront:nil];
		}
		if ([window alphaValue] < 1) {
			[window setAlphaValue:[window alphaValue] +0.1];
		} else {
			[fadeInTimer invalidate];
			[fadeInTimer release];
			fadeInTimer = nil;
			[window setAlphaValue:1];
		}
	} else {
		if ([window alphaValue] > 0) {
			[window setAlphaValue:[window alphaValue] - 0.1];
		} else {
			[fadeOutTimer invalidate];
			[fadeOutTimer release];
			fadeOutTimer = nil;
			[NSApp terminate:self];
		}
	}
}

- (BOOL) applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender{
	return YES;
}

- (BOOL) windowShouldClose:(id)window {
	fadeOutTimer = [[NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(fadeWindow:) userInfo:@"fade_out" repeats:YES] retain];
	return YES;
}

- (IBAction) fixExploit:(id)sender {
	AuthorizationRef authref;
	OSStatus status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authref);

	if (status != noErr) {
		NSLog(@"Could not get authorization, failing.");
		return;
	}

	char *args[3] = { "--noscript", NULL, NULL };
	
	if ([rmSuid state] == NSOnState) {
		args[1] = "--nosuid";
	}

	[progress startAnimation:nil];
	status = AuthorizationExecuteWithPrivileges(authref, [[[NSBundle mainBundle] pathForResource:@"helper" ofType:nil] UTF8String], 0, args, NULL);

	switch (status) {
		case 0:
			[self checkLoop];
			break;
		case -60006:
			NSBeginAlertSheet(@"Authentication error", @"OK", nil, nil, window, nil, nil, nil, nil, @"You canceled the authorization.");
			[progress stopAnimation:nil];
			break;
		case -60005:
			NSBeginAlertSheet(@"Authentication error", @"OK", nil, nil, window, nil, nil, nil, nil, 
							  [NSString stringWithFormat:@"You were denied authorization.\n(Error: %i - Too many attempts)", status]);
			[progress stopAnimation:nil];
			break;
		default:
			NSBeginAlertSheet(@"Authentication error", @"OK", nil, nil, window, nil, nil, nil, nil, 
							  [NSString stringWithFormat:@"There was an error while authorizing.\n(Error: %i)", status]);
			[progress stopAnimation:nil];
			break;
	}
	AuthorizationFree(authref, kAuthorizationFlagDefaults);
}

- (IBAction) showAdvanced:(id)sender {
	if([advButton state] == NSOnState) {
		[rmSuid setHidden: NO];
		[window setFrame:NSMakeRect([window frame].origin.x, [window frame].origin.y - 22, 264, 345) display:YES animate:YES];
	} else {
		[rmSuid setHidden: YES];
		[window setFrame:NSMakeRect([window frame].origin.x, [window frame].origin.y + 22, 264, 323) display:YES animate:YES];
	}
}

- (IBAction) contactDevs:(id)sender {
	[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"mailto:support@theiphoneproject.org?subject=ARD%20Patcher%20Support%20Request"]];
}

- (IBAction) visitWebsite:(id)sender {
	[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://new.theiphoneproject.org/index.php/component/content/article/3-releases/86-ard-patcher"]];
}

@end
